/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.google.code.carrossel.beans;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import static java.lang.Math.sin;
import static java.lang.Math.cos;
import static java.lang.Math.toRadians;

/**
 *
 * @author fcfm
 */
public class Peca extends Canvas implements Serializable, Shape, PathIterator {

    private Point2D.Double center = new Point2D.Double(0, 0);
    private Point2D.Double p1 = null,  p2 = null,  p3 = null,  p4 = null,  p5 = null,  p6 = null;
    private double h = 0,  d = 0;
    private double radius = 50;

    public Peca() {
        calculatePoints();
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        Peca p = new Peca();
        p.setCenter(new Point2D.Double(100, 100));
        p.setRadius(50);
        p.calculatePoints();
        g2d.draw(p);
    }

    /**
     * Function to draw a triangle with a specified color
     * 
     * @param g -
     *            Graphic context to receive drawings
     * @param c -
     *            Color to fill the triangle.
     */
    public void draw(Graphics g, Color c) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(c);
        g2d.fill(this.triangle);
        g2d.setColor(Color.BLACK);
        g2d.setStroke(new BasicStroke(1));
        g2d.draw(this.triangle);
    }

    /**
     * Method that draws a disk given the graphic context and the color array
     * Obs.: Please, reuse the ColorSet enum, when calling this method.
     * 
     * @param g2d -
     *            Graphic context to receive the drawings
     * @param cArray -
     *            Array of colors, 6 colors, to paint a game disk
     */
    public void drawDisk(Graphics2D g2d, Color[] cArray) {
        for (int i = 0; i < cArray.length; ++i) {
            rotate(g2d, 60);
            draw(g2d, cArray[i]);
        }
    }

    public Rectangle2D getBounds2D() {
        Rectangle2D.Double resultBounds = new Rectangle2D.Double();
        resultBounds.x = (int) (getP1().x);
        resultBounds.y = (int) (getP6().y);
        resultBounds.width = (int) (getP4().x - getP2().x);
        resultBounds.height = (int) (getP3().y - getP6().y);
        return resultBounds;
    }

    public boolean contains(double x, double y) {
        boolean part1 = (y < calculateA(getP1(), getP2()) * x + calculateB(getP1(), getP2()));
        boolean part2 = (y > calculateA(getP2(), getP3()) * x + calculateB(getP1(), getP2()));
        boolean part3 = (y > calculateA(getP3(), getP4()) * x + calculateB(getP1(), getP2()));
        boolean part4 = (y > calculateA(getP4(), getP5()) * x + calculateB(getP1(), getP2()));
        boolean part5 = (y < calculateA(getP5(), getP6()) * x + calculateB(getP1(), getP2()));
        boolean part6 = (y < calculateA(getP6(), getP1()) * x + calculateB(getP1(), getP2()));

        if (part1 && part2 && part3 && part4 && part5 && part6) {
            return true;
        } else {
            return false;
        }
    }

    public boolean contains(Point2D p) {

        boolean part1 = (p.getY() <= calculateA(getP1(), getP2()) * p.getX() + calculateB(getP1(), getP2()));
        boolean part2 = (p.getY() <= calculateA(getP2(), getP3()) * p.getX() + calculateB(getP1(), getP2()));
        boolean part3 = (p.getY() >= calculateA(getP3(), getP4()) * p.getX() + calculateB(getP1(), getP2()));
        boolean part4 = (p.getY() >= calculateA(getP4(), getP5()) * p.getX() + calculateB(getP1(), getP2()));
        boolean part5 = (p.getY() >= calculateA(getP5(), getP6()) * p.getX() + calculateB(getP1(), getP2()));
        boolean part6 = (p.getY() <= calculateA(getP6(), getP1()) * p.getX() + calculateB(getP1(), getP2()));

        if (part1 && part2 && part3 && part4 && part5 && part6) {
            return true;
        } else {
            return false;
        }

    }

    public boolean intersects(double x, double y, double w, double h) {
        Point2D p2dTopLeft = new Point2D.Double(x, y);
        Point2D p2dTopRight = new Point2D.Double(x + w, y);
        Point2D p2dBottomLeft = new Point2D.Double(x, y + h);
        Point2D p2dBottomRight = new Point2D.Double(x + w, y + w);
        boolean isp2dTopLeft = contains(p2dTopLeft);
        boolean isp2dTopRight = contains(p2dTopRight);
        boolean isp2dBottomLeft = contains(p2dBottomLeft);
        boolean isp2dBottomRight = contains(p2dBottomRight);

        if (isp2dBottomRight || isp2dBottomLeft || isp2dTopRight || isp2dTopLeft) {
            return true;
        } else {
            return false;
        }
    }

    public boolean intersects(Rectangle2D r) {
        Point2D p2dTopLeft = new Point2D.Double(r.getX(), r.getY());
        Point2D p2dTopRight = new Point2D.Double(r.getX() + r.getWidth(),
                r.getY());
        Point2D p2dBottomLeft = new Point2D.Double(r.getX(), r.getY() + r.getHeight());
        Point2D p2dBottomRight = new Point2D.Double(
                r.getX() + r.getWidth(), r.getY() + r.getHeight());
        boolean isp2dTopLeft = contains(p2dTopLeft);
        boolean isp2dTopRight = contains(p2dTopRight);
        boolean isp2dBottomLeft = contains(p2dBottomLeft);
        boolean isp2dBottomRight = contains(p2dBottomRight);

        if (isp2dBottomRight || isp2dBottomLeft || isp2dTopRight || isp2dTopLeft) {
            return true;
        } else {
            return false;
        }
    }

    public boolean contains(double x, double y, double w, double h) {
        Point2D p2dTopLeft = new Point2D.Double(x, y);
        Point2D p2dTopRight = new Point2D.Double(x + w, y);
        Point2D p2dBottomLeft = new Point2D.Double(x, y + w);
        Point2D p2dBottomRight = new Point2D.Double(x + w, y + w);
        boolean isp2dTopLeft = contains(p2dTopLeft);
        boolean isp2dTopRight = contains(p2dTopRight);
        boolean isp2dBottomLeft = contains(p2dBottomLeft);
        boolean isp2dBottomRight = contains(p2dBottomRight);

        if (isp2dBottomRight && isp2dBottomLeft && isp2dTopRight && isp2dTopLeft) {
            return true;
        } else {
            return false;
        }
    }

    public boolean contains(Rectangle2D r) {
        // The Rectangle2D class describes a rectangle defined by
        // a location (x, y) and dimension (w x h).
        Point2D p2dTopLeft = new Point2D.Double(r.getX(), r.getY());
        Point2D p2dTopRight = new Point2D.Double(r.getX() + r.getWidth(),
                r.getY());
        Point2D p2dBottomLeft = new Point2D.Double(r.getX(), r.getY() + r.getHeight());
        Point2D p2dBottomRight = new Point2D.Double(
                r.getX() + r.getWidth(), r.getY() + r.getHeight());
        boolean isp2dTopLeft = contains(p2dTopLeft);
        boolean isp2dTopRight = contains(p2dTopRight);
        boolean isp2dBottomLeft = contains(p2dBottomLeft);
        boolean isp2dBottomRight = contains(p2dBottomRight);

        if (isp2dBottomRight && isp2dBottomLeft && isp2dTopRight && isp2dTopLeft) {
            return true;
        } else {
            return false;
        }
    }

    public PathIterator getPathIterator(AffineTransform at) {
        GeneralPath gn = new GeneralPath();
        gn.moveTo(getCenter().x, getCenter().y);
        gn.lineTo(getP1().x, getP1().y);
        gn.lineTo(getP2().x, getP2().y);
        gn.lineTo(getP3().x, getP3().y);
        gn.lineTo(getP4().x, getP4().y);
        gn.lineTo(getP5().x, getP5().y);
        gn.lineTo(getP6().x, getP6().y);
        gn.lineTo(getP1().x, getP1().y);

        gn.moveTo(getCenter().x, getCenter().y);
        gn.lineTo(getP1().x, getP1().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP2().x, getP2().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP3().x, getP3().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP4().x, getP4().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP5().x, getP5().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP6().x, getP6().y);
        gn.lineTo(getCenter().x, getCenter().y);
        gn.lineTo(getP1().x, getP1().y);
        return gn.getPathIterator(at);
    }

    public PathIterator getPathIterator(AffineTransform at, double flatness) {
        return getPathIterator(at);
    }

    public int getWindingRule() {
        return getPathIterator(new AffineTransform()).getWindingRule();
    }

    public boolean isDone() {
        return getPathIterator(new AffineTransform()).isDone();
    }

    public void next() {
        getPathIterator(new AffineTransform()).next();
    }

    public int currentSegment(float[] coords) {
        return getPathIterator(new AffineTransform()).currentSegment(coords);
    }

    public int currentSegment(double[] coords) {
        return getPathIterator(new AffineTransform()).currentSegment(coords);
    }

    /*
     * Métodos utilitários
     * 
     */
    /**
     * a linear function can be characterized by its 2 arguments a and b (f(x) =
     * ax + b) this function receives two Points (instance of Point2D.Double)
     * and returns the a.
     * 
     * @return "a" parameter of a linear function
     */
    private double calculateA(Point2D.Double point1, Point2D.Double point2) {
        double result = 0;
        result = (point2.getY() - point1.getY()) / (point2.getX() - point1.getX());
        return result;
    }

    /**
     * a linear function can be characterized by its 2 arguments a and b (f(x) =
     * ax + b) this function receives two Points (instance of Point2D.Double)
     * and returns the b.
     * 
     * @return "b" parameter of a linear function
     */
    private double calculateB(Point2D.Double point1, Point2D.Double point2) {
        double result = 0;
        result = point2.getY() - calculateA(point1, point2) * point2.getX();
        return result;
    }

    private void calculatePoints() {
        setD();
        setH();
        setP1(new Point2D.Double(center.getX() - getD(), center.getY() - getH()));
        setP2(new Point2D.Double(center.getX() - getRadius(), center.getY()));
        setP3(new Point2D.Double(center.getX() - getD(), center.getY() + getH()));
        setP4(new Point2D.Double(center.getX() + getD(), center.getY() + getH()));
        setP5(new Point2D.Double(center.getX() + getRadius(), center.getY()));
        setP6(new Point2D.Double(center.getX() + getD(), center.getY() - getH()));
    }
    // Fim dos métodos utilitários
    /*
     *Getters and setters 
     */
    public Double getCenter() {
        return center;
    }

    public void setCenter(Double center) {
        this.center = center;
    }

    public double getD() {
        return d;
    }

    public void setD() {
        this.d = getRadius() * cos(toRadians(60));
    }

    public double getH() {
        return h;
    }

    public void setH() {
        this.h = getRadius() * sin(toRadians(60));
    }

    public Double getP1() {
        return p1;
    }

    public void setP1(Double p1) {
        this.p1 = p1;
    }

    public Double getP2() {
        return p2;
    }

    public void setP2(Double p2) {
        this.p2 = p2;
    }

    public Double getP3() {
        return p3;
    }

    public void setP3(Double p3) {
        this.p3 = p3;
    }

    public Double getP4() {
        return p4;
    }

    public void setP4(Double p4) {
        this.p4 = p4;
    }

    public Double getP5() {
        return p5;
    }

    public void setP5(Double p5) {
        this.p5 = p5;
    }

    public Double getP6() {
        return p6;
    }

    public void setP6(Double p6) {
        this.p6 = p6;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }
}
